//@author: Gabriele Cerfoglio


package sa4

import javax.servlet.http._
import scala.xml._
import javax.imageio.ImageIO
import javax.servlet.ServletConfig
import sa4.persistence._
import org.squeryl.KeyedEntity
import org.squeryl.SessionFactory
import org.squeryl.Session
import org.squeryl.adapters.H2Adapter
import scala.util.matching.Regex
import org.squeryl.adapters.H2Adapter
import java.sql.Timestamp
import java.sql.Date
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.dsl.ast.LogicalBoolean
import org.squeryl.annotations.OptionType
import java.util.concurrent.ConcurrentLinkedQueue
//import javax.servlet.AsyncContext



//User for car app
class CarUser(val firstName: String, val lastName: String, val username: String, val password: String)  {
	def this() = this("", "", "", "")
	val id = username
}

//Car journey
class CarJourney(val departure: String, val destination: String, val date:String, val description: String, val user:String) extends KeyedEntity[Long] {
	def this() = this("", "", "", "", "")
	val id = 0L
}

//Partecipants of journey
class Partecipant(val journeyId:Long, val user:String, val status:String) extends KeyedEntity[Long] {
	def this() = this(0, "", "")
	val id = 0L
}

//Schema for car app
object CarSchema extends org.squeryl.Schema {
	val users = table[CarUser]
	val journeys = table[CarJourney]
	val partecipants = table[Partecipant]
}



class CarApp extends Framework {

	// Fixing the quotation problems with Scala
	def fixQuotes(template: NodeSeq): NodeSeq = {
			import scala.xml.transform._
			object FixQuotes extends BasicTransformer {
				override protected def unchanged(n: Node, ns: Seq[Node]) = ns.length == 1 && (ns.head eq n)
				override def transform(n: Node): Seq[Node] = n match {
				case n: Text => new Unparsed(n.text)
				case n => super.transform(n)
				}
			}
			FixQuotes.transform(template)
	}

	override def init(config: ServletConfig) = {
		super.init(config)
		initRoutes

		transaction {
			//Test Database
			CarSchema.drop
			CarSchema.create
			
			CarSchema.users.insert(new CarUser("Simple", "Person", "SimpleP", "banana"))
			CarSchema.users.insert(new CarUser("John", "Smith", "JohnS", "banana"))
			CarSchema.users.insert(new CarUser("Bill", "Gates", "BillyG", "banana"))
			CarSchema.users.insert(new CarUser("Steve", "Jobs", "StewieJ", "banana"))
			CarSchema.users.insert(new CarUser("Mister", "Anderson", "Neo", "banana"))
			CarSchema.users.insert(new CarUser("Some", "Dude", "SomeD", "banana"))
			
			CarSchema.journeys.insert(new CarJourney("Lugano", "Milano", "11/09/2012", "Traffico a balla", "SimpleP"))
			CarSchema.journeys.insert(new CarJourney("Genova", "Padova", "19/11/2011", "Ancora traffico", "BillyG"))
			CarSchema.journeys.insert(new CarJourney("Tokyo", "Pechino", "22/01/2012", "Impossibile", "StewieJ"))
			CarSchema.journeys.insert(new CarJourney("Terra", "Luna", "11/09/2092", "...?", "Neo"))
			CarSchema.journeys.insert(new CarJourney("X", "Y", "11/09/3052", "?!?", "SomeD"))
			
			CarSchema.partecipants.insert(new Partecipant(1, "JohnS", "Pending"))
		}
	}

	override def service(req: HttpServletRequest, resp: HttpServletResponse) {
		super.service(req, resp)
		//Session setup
		var currentContext = session.getCurrentContext
		currentContext = session.newContext(req, resp, currentContext)
		var cc = currentContext().session
		cc.setMaxInactiveInterval(60)
	}

	def initRoutes(): Unit = {

			initDefaultRoutes()
			
			routing.addRoute("/home", homePage)
			routing.addRoute("/usersList", usersListPage)
			routing.addRoute("/profile", profilePage)
			routing.addRoute("/newJourney", newJourneyPage)
			routing.addRoute("/register", newUserPage)
			routing.addRoute("/login", loginPage)
			routing.addRoute("/join", join)
			routing.addRoute("/allow", allow)
			routing.addRoute("/error1", errorPage1)
			routing.addRoute("/error2", errorPage2)
			
	}

	def initDefaultRoutes(): Unit = {
			routing.addRoute("/", startPage)

			//Routings for twitter.bootstrap CSS
			//-------------------------->
			routing.addRoute("/bootstrap-responsive.css", bootstrapResponsive)
			routing.addRoute("/bootstrap-responsive.min.css", bootstrapResponsiveMin)
			routing.addRoute("/bootstrap.css", bootstrap)
			routing.addRoute("/bootstrap.min.css", bootstrapMin)
			//<-------------------------
		
	}

	// Loads the startup page
	def startPage(req: HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
		val xml = template.loadPage("src/main/scala/sa4/carIndex.html")
		val html = form.prepareForm(xml, routing)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
		resp.setContentType("text/html")
	}
	
	// Loads the home page once logged in
	def homePage(req: HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
		val cookie = req.getCookies()(1)
		println(cookie.getValue())
		val xml = template.loadPage("src/main/scala/sa4/carHome.html")
		val id = session.getIdSession
		transaction {
			val u = from(CarSchema.users)(user =>
			where(user.id === cookie.getValue())
			select (user.username))
			val name = u.head
			val new_xml = template.processTemplate(journeyList(name))
			val html = form.prepareForm(new_xml, routing)
			resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
			resp.setContentType("text/html")
		}
	}
	
	// Loads the users list page
	def usersListPage(req: HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
		val xml = fixQuotes(template.processTemplate(usersList))
		val html = form.prepareForm(xml, routing)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
	}
	
	// Loads the profile page
	def profilePage(req: HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
		val cookie = req.getCookies()(1)
		transaction {
			val user = from(CarSchema.users)(s => where(s.username === cookie.getValue()) select (s))
			val xml = fixQuotes(template.processTemplate(profileXML(user.single)))
			val html = form.prepareForm(xml, routing)
			resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
		}	
	}
	
	// Loads the page to create a new journey
	def newJourneyPage(req:HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
	    val xml = fixQuotes(template.processTemplate(newJourneyXML))
		val html = form.prepareForm(xml, routing)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
	}
	
	// Loads the page to add a new user
	def newUserPage(req:HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
	    val xml = fixQuotes(template.processTemplate(newUserXML))
		val html = form.prepareForm(xml, routing)
		resp.setContentType("text/html")
		resp.setStatus(HttpServletResponse.SC_OK)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
	}
	
	// Loads the login page
	def loginPage(req:HttpServletRequest, resp: HttpServletResponse) {
		session.updateSession
	    val xml = fixQuotes(template.processTemplate(loginXML))
		val html = form.prepareForm(xml, routing)
		resp.setContentType("text/html")
		resp.setStatus(HttpServletResponse.SC_OK)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
	}
	
	// Loads the pre-login error pages
	def errorPage1(req:HttpServletRequest, resp: HttpServletResponse) {
	    session.updateSession
	    val message = req.getParameter("message")
	    val xml = fixQuotes(template.processTemplate(errorXML1(message)))
		val html = form.prepareForm(xml, routing)
		resp.setContentType("text/html")
		resp.setStatus(HttpServletResponse.SC_OK)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
	}
	
	// Loads the post-login errror pages
	def errorPage2(req:HttpServletRequest, resp: HttpServletResponse) {
	    session.updateSession
	    val message = req.getParameter("message")
	    val xml = fixQuotes(template.processTemplate(errorXML1(message)))
		val html = form.prepareForm(xml, routing)
		resp.setContentType("text/html")
		resp.setStatus(HttpServletResponse.SC_OK)
		resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
	}
	
	// Handler to add a new journey to the DB
	def addNewJourney(req: HttpServletRequest, resp: HttpServletResponse) {
		val departure = req.getParameter("departure")
		val destination = req.getParameter("destination")
		val date = req.getParameter("date")
		val description = req.getParameter("description")
		val cookie = req.getCookies()(1)
		val organiser = cookie.getValue()
		if(departure.length() <= 1 || departure.length() <= 1 || date.length() <= 1 || description.length() <= 2) {
		  resp.sendRedirect("/error2?message=InvalidDetails")
		}
		transaction {
			CarSchema.journeys.insert(new CarJourney(departure, destination, date, description, organiser))
		}
		resp.sendRedirect("/home")
	}

	// Handler to add a new user to the DB
	def addUser(req: HttpServletRequest, resp: HttpServletResponse) = {
		val firstName = req.getParameter("firstName")
		val lastName = req.getParameter("lastName")
		val username = req.getParameter("username")
		val password = req.getParameter("password")
		if(firstName.length() <= 1 || lastName.length() <= 1 || username.length() <= 1 || password.length() <= 2) {
		  resp.sendRedirect("/error1?message=InvalidDetails")
		}
		transaction {
		  var validUsername = true
		  for (s <- from(CarSchema.users)(s => where(s.username === username) select (s))) yield {
				validUsername = false
			}
		  if (validUsername) {
		    CarSchema.users.insert(new CarUser(firstName, lastName, username, password))
		    resp.sendRedirect("/")
		  }
		  else {
		    resp.sendRedirect("/error1?message=UsernameAlreadyExist")
		  }
		}
	}
	
	// Handler to add a new partecipant to a journey in the DB
	def join(req: HttpServletRequest, resp: HttpServletResponse) = {
		val journeyID = req.getParameter("journeyID").toLong
		val cookie = req.getCookies()(1)
		val username = cookie.getValue()
		var alreadyJoined = false
		transaction {
			for (s <- from(CarSchema.partecipants)(s => where(s.journeyId === journeyID and username === s.user) select (s))) yield {
				alreadyJoined = true
			}
			if(alreadyJoined) {
				resp.sendRedirect("/error2?message=AlreadyJoined")
			}
			else {			  
				CarSchema.partecipants.insert(new Partecipant(journeyID, username, "Pending"))
				resp.sendRedirect("/home")
			}
		}
	}
	
	// Handler to allow a new partecipant to a journey in the DB
	def allow(req: HttpServletRequest, resp: HttpServletResponse) = {
		val id = req.getParameter("id").toLong
		transaction {
		  update(CarSchema.partecipants)(s => where(s.id === id) set(s.status := "Confirmed"))
		  resp.sendRedirect("/home")
		}
		
	}
	
	// The login handler
	def loginMethod(req: HttpServletRequest, resp: HttpServletResponse) {
		val u = req.getParameter("username")
		val p = req.getParameter("password")
		if(u.length() <= 1 || p.length() <= 1) {
		  resp.sendRedirect("/error1?message=InvalidLogin")
		}
		var validLogin = false 
		transaction {
			for (s <- from(CarSchema.users)(s => where(s.username === u and s.password === p) select (s))) yield {
				validLogin = true
			}
		}
		if(validLogin) {
			println(u)
			var c = new Cookie("user", u)
			c.setPath("/")
			resp.addCookie(c)
			var dest = resp.encodeRedirectURL("/home")
			resp.sendRedirect(dest)
		}
		else {
			resp.sendRedirect("/error1?message=InvalidLogin")
		}
	}
	
	///////////////////////////
	// Methods for loading twitter-Bootstrap CSS
	//------------------------>
	def bootstrapResponsive(req: HttpServletRequest, resp: HttpServletResponse) {
		val css = template.loadFile("src/main/scala/sa4/bootstrap/css/bootstrap-responsive.css")
		resp.getWriter.write(css)
		resp.setContentType("text/css")
	}

	def bootstrapResponsiveMin(req: HttpServletRequest, resp: HttpServletResponse) {
		val css = template.loadFile("src/main/scala/sa4/bootstrap/css/bootstrap-responsive.min.css")
		resp.getWriter.write(css)
		resp.setContentType("text/css")
	}

	def bootstrap(req: HttpServletRequest, resp: HttpServletResponse) {
		val css = template.loadFile("src/main/scala/sa4/bootstrap/css/bootstrap.css")
		resp.getWriter.write(css)
		resp.setContentType("text/css")
	}

	def bootstrapMin(req: HttpServletRequest, resp: HttpServletResponse) {
		val css = template.loadFile("src/main/scala/sa4/bootstrap/css/bootstrap.min.css")
		resp.getWriter.write(css)
		resp.setContentType("text/css")
	}
	//<--------------------------

	
	// XML to diplay all the journeys in the DB
	def journeyList(user: String)={
			<surround with="src/main/scala/sa4/carHome.html" at="here">
			<div class="container">
			<div class="hero-unit">
			<h1>Welcome {user}</h1>
			<hr/>
			<a class="btn btn-primary" href="/newJourney">Offer car ride</a>
			</div>
			<p>
			{
				transaction {
					for (s <- from(CarSchema.journeys)(s => where(s.id === s.id) select (s))) yield {
						<div class="hero-unit">
						<h1>{ s.departure } - {s.destination}</h1>
						<hr/>
						{for (r <- from(CarSchema.users)(r => where(s.user === r.username) select (r))) yield {
							<h2>{r.username}</h2>
						}}
						<h2>{ s.date }</h2>
						<p>{s.description}</p>
						<hr/>
						{for (p <- from(CarSchema.partecipants)(p => where(s.id === p.journeyId) select (p))) yield {
							<p>{p.user}: {p.status}</p>
						}}
						{if(s.user != user) {<a class="btn btn-primary" href={"/join?journeyID=" + s.id}>Join</a>}}
						</div>                     	
					}
				}
			}
			</p>
			</div>
			</surround>
			//<--------------------------

	}
	
	
	// XML to display a list of users
	def usersList = {
			<surround with="src/main/scala/sa4/carHome.html" at="here">
			<p>
			<div class="hero-unit">
			<ul>
			{
				transaction {
					for (s <- from(CarSchema.users)(s => where(s.id === s.id) select (s)).distinct) yield {
						<li>{s.username}</li>
					}
				}
			}
			</ul>
			</div>
			</p>
			</surround>
			//<--------------------------
	}
	
	// XML for the new Journey page
	def newJourneyXML = {
			<surround with="src/main/scala/sa4/carHome.html" at="here">
			<div class="container">
			<p>
			<div class="hero-unit">
			<h1>Offer car ride</h1>
			<hr/>
			<form id="form-registration" method="post" handler="addNewJourney(sa4.CarApp)">
			<p>
			<span>Departure: </span>
			<input type="text" name="departure" />
			</p>
			<p>
			<span>Destination: </span>
			<input type="text" name="destination"/>
			</p>
			<p>
			<span>Date: </span>
			<input type="text" name="date"/>
			</p>
			<p>
			<span>Description: </span>
			<input type="text" name="description"/>
			</p>
			<p>
			<input class="btn btn-primary" type="submit" value="Create Journey" id="createJourney"/>
			</p>
			</form>
			</div>
			</p>
			</div>
			</surround>
	}
	
	// XML for the registration page
	def newUserXML = {
			<surround with="src/main/scala/sa4/carIndex.html" at="here">
			<div class="container">
			<p>
			<div class="hero-unit">
			<h1>Register</h1>
			<hr/>
			<form id="form-registration" method="post" handler="addUser(sa4.CarApp)">
			<p>
			<span>First Name: </span>
			<input type="text" name="firstName" />
			</p>
			<p>
			<span>Last Name: </span>
			<input type="text" name="lastName"/>
			</p>
			<p>
			<span>Username: </span>
			<input type="text" name="username"/>
			</p>
			<p>
			<span>Password: </span>
			<input type="password" name="password"/>
			</p>
			<p>
			<input class="btn btn-primary" type="submit" value="Create Account" id="createAccount"/>
			</p>
			</form>
			</div>
			</p>
			</div>
			</surround>
	}
	
	// XML for the login page
	def loginXML = {
			<surround with="src/main/scala/sa4/carIndex.html" at="here">
			<div class="container">
			<p>
			<div class="hero-unit">
			<h1>Login</h1>
			<hr/>
			<form id="form-registration" method="post" handler="loginMethod(sa4.CarApp)">
			<p>
			<span>Username: </span>
			<input type="text" name="username"/>
			</p>
			<p>
			<span>Password: </span>
			<input type="password" name="password"/>
			</p>
			<p>
			<input class="btn btn-primary" type="submit" value="Login" id="login"/>
			</p>
			</form>
			</div>
			</p>
			</div>
			</surround>
	}
	
	def profileXML(profileUser:sa4.CarUser) = {
			<surround with="src/main/scala/sa4/carHome.html" at="here">
			<div class="container">
			<p>
			<div class="hero-unit">
			<h1>{profileUser.username}</h1>
			<hr/>
			<h3>{profileUser.firstName} {profileUser.lastName}</h3>
			</div>
			<h1>Created</h1>
			{
			transaction {
				for (s <- from(CarSchema.journeys)(s => where(s.user === profileUser.username) select (s)).distinct) yield {
					<div class="hero-unit">
					<h2>{ s.departure } - {s.destination}</h2>
					<hr/>
					{for (r <- from(CarSchema.users)(r => where(s.user === r.username) select (r))) yield {
						<h3>{r.username}</h3>
					}}
					<h3>{ s.date }</h3>
					<p>{s.description}</p>
					<hr/>
					{for (p <- from(CarSchema.partecipants)(p => where(s.id === p.journeyId) select (p))) yield {
					  {if(p.status == "Pending") {<p>{p.user}: Pending  <a class="btn" href={"/allow?id=" + p.id}>Confirm</a> </p>}
					  else {<p>{p.user}: Confirmed</p>}}
					}}
					</div>                     	
				}}}
			<h1>Joined</h1>
			{
			transaction {
				for (a <- from(CarSchema.partecipants)(s => where(s.user === profileUser.username) select(s)).distinct) yield {
					for (s <- from(CarSchema.journeys)(s => where(s.id === a.journeyId) select (s)).distinct) yield {
						<div class="hero-unit">
						<h2>{ s.departure } - {s.destination}</h2>
						<hr/>
						{for (r <- from(CarSchema.users)(r => where(s.user === r.username) select (r))) yield {
							<h3>{r.username}</h3>
						}}
						<h3>{ s.date }</h3>
						<p>{s.description}</p>
						<hr/>
						{for (p <- from(CarSchema.partecipants)(p => where(s.id === p.journeyId) select (p))) yield {
							<p>{p.user}: {p.status}</p>
						}}
						</div>                     	
					}}}
			}
			</p>
			</div>
			</surround>
	}
	
	// XML for the pre-login error pages
	def errorXML1(message: String) = {
			<surround with="src/main/scala/sa4/carIndex.html" at="here">
			<h1>
			{message}
			</h1>
			</surround>
	}
	
	// XML for the post-login error pages
	def errorXML2(message: String) = {
			<surround with="src/main/scala/sa4/carHome.html" at="here">
			<h1>
			{message}
			</h1>
			</surround>
	}
}